2019 SDC 议题回顾 | 是谁推开我的“窗”:iOS App接口安全分析
对于广大的开发者而言,WebView和URL Scheme并不陌生。WebView一直以来都是iOS的核心组件,而URL Scheme是iOS APP重要的接口之一。
由于目前接口安全性研究较少,已有的研究也主要集中在Android系统,忽略了iOS 应用的安全性。下面就让我们来回顾看雪2019安全开发者峰会上《是谁推开我的“窗”:iOS App接口安全分析》的精彩内容。
编辑按
crownless:在广阔的网络世界,或许你在浏览器上的一次点击都会触发你手机上已安装的应用的漏洞,进而导致账号被盗、财产丢失、数据泄漏……
导致这个问题的原因是开发者没有在外部可调用接口上部署权限检查或过滤有害调用。所以,开发者应当时刻将安全放在首要的位置上!
嘉宾介绍
张一峰,北京长亭科技移动安全负责人,负责移动APP安全审计、源码审计等漏洞挖掘工作。全球互联网技术大会网络安全专场演讲嘉宾,2018华为终端安全奖励计划大会圆桌会议嘉宾,2018 DEFCON Demo Labs speaker。
大会上讲师首次披露了由于iOS APP URL Scheme和JSBridge接口导致的安全漏洞,完美的体现了“不知攻焉知防”。通过把过程接口调用可以利用的方式,在iOS平台上实现可攻击的效果和cookie注入等方式,最后还帮我们做了防范和总结,希望大家注意iOS开发里的那几点。
演讲具体内容
以下为速记全文:
大家好!我是来自长亭科技的张一峰,我分享的题目是APP接口安全分析。
议题主要内容有这么五方面:第一,业务开发模式变迁,第二,混合开发模式,第三,漏洞成因和漏洞基础,第四,实例展示,因为接口设计的缺陷导致的安全问题,第五,总结以及开发建议。
一
移动开发模式变迁
在开发模式变迁的时间轴,上面是一些时间节点,下面是相对应时间段在移动开发中Android和iOS使用的主流开发语言。
2007年Android和iOS问世,最初主要以java和oc作为编程语言,随后谷歌增加了c和c++的支持。后面由于html5技术的发展,逐渐出现了纯WEB的应用,不只是移动端,其它也会有,苹果在这期间推出了它的新开发语言swift,同时,谷歌也官方支持了kotlin语言进行开发。
但是由于WEBAPP存在缺陷,后面人们更多使用混合开发模式,也就是当前比较多的开发模式。截止到现在,从最初的java、oc,到现在官方主流推荐使用kotlin和swift开发,所以经过10多年变迁,有很大变化。同时,因为有混合开发模式,还有着js和HTML。
二
混合开发模式
从时间线来说,从原生到WEB再到混合开发模式,但是从逻辑角度来说,混合开发模式放在中间是更合适的。原生开发模式很好理解,我们用官方提供的语言,调用Framework的接口实现我们APP的功能。在webAPP里完全依赖于webview这个组件使用纯前端技术,我们APP的功能和逻辑主要由js实现。
而混合开发模式是原生和web的混合,它相当于二者间的一个结合点。主流APP的调用还是依靠原生代码进行调用,对于一些业务应用,比如更新比较频繁的,或者可能每天都会变化的业务,我们会使用纯web进行展示和渲染。
为了引出混合开发模式,就不得不提webAPP里面的优缺点,优点是跨平台、开发成本比较低,但缺点也很多,其中比如主要的就是它对复杂算法、多编程等等支持不是很好。
同时,因为js能做的最多是压缩和混淆,它对以前的代码而言,与编译之后的二进制相比,对源代码保护还是有很多不足的。同时,有个最重要的缺点是与原生API交互是非常不方便的。
基于这个原因,人们开始逐渐使用混合开发模式,混合开发模式继承了webAPP里的很多优点,通过JSBridge手段避免了很多API调用的问题。JSBridge是什么?它是js代码到原生代码的接口,大家把它形容为像一个桥一样。
通过这个桥,我们的API还是由原生的代码进行调用。其实JSBridge的功能并不是在混合开发模式中从零做出来的,它类似的功能在系统API本来就已经提供了,只不过在混合开发模式中程序员对API进行了一定的封装和扩展。
下面举个能实现这样功能的API,上面是UIWebView,下面是WKWebView,可以实现JSBridge的功能,后面还有相应API的例子,这里面就不再详细说了。
基于这个API有一个使用很多的框架,就是这个WebViewJavascriptBridage框架,它就是对API进行封装,跟着框架所做的主要贡献是增加了回调函数,也就是说它能获取JSBridge的返回值。
正常来说,我们通过这个API只能进行传参、调用,但是如果获取的JSBridge返回值,所以在复杂业务里是有缺陷的,这个框架主要是增加callback,能通过callback获取它的返回值。
另外一个是UIWebView的JavaScriptCore和WKScriptMessageHandler,它也都分别有对应的API可以实现这样的功能。后面有例子,这里就再不继续展开。
说到这样的功能的API就不得不提安卓平台下这个接口,这个接口也是为了实现同样的接口,但是在2012年被人们发现它可以实现一个远程任意代码执行,这个漏洞影响特别深远,很多做开发的哪怕不懂安全,但在写代码时都会用一个API判断是否大于17来避开这个漏洞,其实这个地方是容易发生安全问题的。
三
漏洞成因和漏洞接触
首先介绍一下iOS deeplinks里的一种,就是URLScheme,它是提供了应用间IPC的方式,也就是说通过URLScheme可以调取另外一个应用再进行返回。怎么使用URLScheme这个技术?其实非常简单,只需要在xcode中进行注册,然后实现回调就可以了。
这个URLScheme大概长这个样子,它是遵从这三个规范,通过API可以传回URLScheme里哪个字段的值,一个应用如果自定义了URLScheme,其他第三方应用就可以对它进行调用,同时网页也可以进行调用。这个也是我们后面例子中远程进行攻击的一个非常依赖的点。
其实URLScheme也不是大家随便可以调的,它是有一个权限限制的,就是需要用户交互的,类似于让用户授权一样,如果通过网页进行调取的话,需要用户点击打开,如果是应用的话,在第一次是需要用户进行授权的。
但那个授权其实相对来说是一个很宽泛的,它并不像强制访问控制,它只是一个需要用户点击和确认就可以了,用户很难去分辨。
说到URLScheme,我们下相对应的代码怎么去用它,前两行代码是URLScheme里面,已知URLScheme,去调取的话很简单的两行代码就可以用这个open函数进行调用了,如果是js的话也非常简单,一个链接就可以了,我们可以通过js里面这种自动点击的方式进行自动调用也可以。
被动应用会进行回调函数,也就是最下面的函数,这个函数里它会获取传输URLScheme的参数,然后根据这个参数再执行相应的逻辑。
刚刚说有几个API可以实现JSBridge的功能,这里选了两个API,选了两个例子,让大家对JSBridge实现有个了解。上面是我们的前端链接,这里面Scheme是自定义的一种,并不是常见的。
当这个链接被请求的时候,系统的这个函数就会被毁掉,相当于对我们请求的链接进行拦截。如果我们这里面自定义了一个Scheme,这里就可以判断这是不是我们自定义的Scheme,也就是说前后端对应好就可以实现通信,这里可以把传输参数取到,实现类似于伪协议的这样一个通信。
通过这个可以实现从前端到后端的调用。另外一个是JScafffun里的函数,它和上一个区别是你可以自定义这个函数的名字,前端你可以认为它是JS函数进行调用,但是它其实也会进入这个代码,在这个代码可以进行获取参数。
有了上面那些基础知识,就引出了接口漏洞的成因:首先,因为自定义的第三方URLScheme,实际应用中你可以去市场中看,基本都有,基本是可以满足的。
定义URLScheme以后,其他的应用或者网页可以对它进行调用,调用完程序可以执行URLScheme和对应回调函数,这个回调本身可能就存在一些缺陷。
如果URLScheme回调函数本身没有什么特别的,我们需要关注它是不是有可能加载任意的URL,如果能加载任意的URL,也就是说我们可以执行我们任意的JS代码,执行任意的JS代码,如果恰好实现很多JSBridge,我们就可以对JSBridge接口进行调用,这种接口可能存在缺陷。两种情况都不符合的话,有其他方式允许应用加载这一段。
为了更好理解下一部分实例里的代码,这里简单讲一下OC运行时。OC是面向对象语言,它是一个大家接触比较多的消息转发机制,它在汇编层面发生函数对话时,它并不是直接把PC指针跳转到目标函数的地址,它是有个OBJC_MSGSEND的函数,把这个函数调用封装,但这个函数调用其实还是通过这个跳转过去,但是它相当于把这个消息进行转发,其实是oc里多态的一种实现。
所以oc里面的多态相当于是在这运行池进行确定的。除此以外,运行池还提供performSelector的方式,Selector可以理解为函数名字,但其实不是。如果把这个函数名传里面去也可以实现函数调用,你不需要在程序里用形式把它包起来。
四
案例分析
案例1:远程窃取cookie的漏洞。
我们首先看它的info.pilist的文件,因为最根本是在这个文件里。这里面我们发现它有一个自定义的URLScheme,有了这个URLScheme以后我们就要去看它的回调函数,它的回调函数逻辑很长,这里只是把相关的函数摘出来了,我们会发现它会进入APPSchemeManager这里,这里是做什么呢?它首先把我们传输的URLScheme以字符段的形式取出,然后拿到问号之后的字符,然后以dictionary的形式进行解析。
因为URLScheme是我们传入的,所以这是一个可控变量。这段所要需要查询的dictionary,就是在标准URLScheme里query这个字段,它是dictionary格式的。进而判断dictionary是否有Key为type的字段,这跟我们没关系,就跳过了。
我们主要看它的eLse值,Else值里会判断是不是有Key的URL,如果有的话它继续判断是不是这个开头,如果两个check都符合的话,它会把这个value传给baseWebViewCONTRoller。
下面是前端的代码,相当于是把这个View压到栈的最上面,把它渲染和展示。有了这个代码我们就知道最终需要传输的URLScheme就是这个样子,我们的K是URL,然后对应的value,前面是字符串URL,后面是链接。这个程序把我们传输的链接会进行渲染出来,然后同时也没有进行任何的校验。
我们让它打开我们的链接并不是真正的目的,所以我们还需要继续往下分析,如果它打开我们的链接,因为这个服务器是我们自己的,我们可以在这里写任意的js,任如果它定义了JSBridge接口的话,我们就可以调用它的JSBridge接口。
我们看一下这个URLSchemeJSBridge,为了实现这个远程攻击,你可以把这个URLScheme写到一个网页里。分析时发现它在初始阶段进行初始化了,初始化最重要的是这个registerLoginWithHanler这个地方,它是前面说到那个框架里面的用法,在这里面相当于前面也注册了名字就是函数的名字,然后第二个是函数名字所对应的操作。
所以最终发现在我们加载页面里实现的效果,我们可以调用Module这个类下面的任意函数。
既然可以实现函数调用,我们就看看这个类下面有什么函数值得我们调,发现了有这个Bridge这个函数,根据函数名字感觉像获取用户信息,我们就需要去验证一下是不是我们想要的样子。所以尝试一下,然后断下来,发现它传输的有电话号码、UID、comparyID和token。
最后payload就呼之欲出了,我们调用getuserinfo接口,达到最终的数组,这个数组不是最终目的,我们把它发给远程服务器。这个页面里通过这个方式,把拿到的token和number发送给我们的服务器。
因为这个页面本身就是www,所以它是符合策略的。这是我们实际在服务端收到一个请求,发现token和手机号都可以发送给我们。拿到token以后相当于对这个帐号实现了控制,而且在移动端里有个特点,就是token的有效期比较长。
案例2:任意文件上传漏洞。
我们还是看info.plist,它有自定义的URLscheme,这个案例本身可以完全不依赖于加载自己页面本身,这可以完全挑过,因为这相当于实现问题比较多,从很多方面都可以去打。
同时为了实现远程攻击,你也可以把URLscheme嵌到网页里。说到URLscheme嵌到网页里刚刚忽略了一个点,它需要有授权,这里最方便的方式是可以以领红包等等之类的方式,因为使用手机的都知道,基本所有都在用URLscheme的应用,比如第三方支付、朋友圈分享等等底层都会用到这个机制,所以对普通用户来说,很难分辨一个跳转是恶意的还是真正的业务功能。
这个里面它使用的shouldStartLoadWithRequest。这个处理,传输的最终会进入hander的URL里面,在这个里面它会首先判断这个URLscheme是不是等于空,如果等于的话它进一步查询URL参数进入handlemessage,进入下面的这个函数。
在这个函数里它获取里面的action和args,这时我们还不知道它是干什么用的,但是我们知道URL是我们传入的,这个是个可控变量,也就是说它所查询到的参数也是我们可控的。这里面它有一个权限验证,就是它判断你是不是有权限调用这个API,但是这个验证是存在缺陷的。
然后就进入下一步,也就是说这两个参数我们知道是可控的。这里面首先会调用权限调用这个函数,这个函数里面会把action前面加一个jsapi_completion,把构造好的action传给performSelector,这个我们在刚刚讲oc运行池时提过这个函数,这个函数传入的相当于是对这个的调用。
分析到这,我们就分析它其实可以实现的效果是对component下面的以“jsapi-”为开头,以completion为结尾的任意函数调用,我们就可以利用这个任意函数调用去做些有意思的事情。
所以我们要分析这类里面到底有什么函数值得我们调用。我们发现它有一个UPloadImages的函数,因为它符合这个命名,我们分析这个函数,发现它首先会拿到这个函数的URL字段,还有一个path字段,其实这个URL是它上传图片目标的服务器地址,path是你要上传文件的路径。有了上面一些已知信息,我们最终的就出来了。
开头是jsapi,也就是我们过它第一个check,action是你所要调用的函数名,我们这里面传入刚刚看到的uploadimages,在这里面要传送两个关键字,一个是URL,一个是path,URL是我们自己的服务器,也就是说把沙箱里的文件上传到我们自己的服务轻易松手,path是沙箱里的一个文件。
所以在POC里上传沙箱里的SQLline数据库。这个例子里面为什么刚刚我说其实不需要加载任意URL那个地方?因为你完全可以把上面的load写在最初的网页里,只要跳转这个目标应用,攻击在一瞬间就完成了。
分析了很多ios平台发现,发现回调函数里利用OC运行时进行函数动态调用的非常多,占比最大。这个例子可以看出你传输的字符是不是以某个数字开始,如果以某个数字开始就会调用某个函数,所以相当于传输一串数字就可以调用里面很多函数。实际分析下来,确实有很多这样的案例存在,是一个非常危险的东西。
案例3:业务逻辑漏洞。
因为之前我们已经对怎么去确定URLscheme是什么样子的,所以在这里我们就把这两个都跳过了,因为可是于这几个实例是内容依次变少了,因为有很多东西思路是重复的,但代码不一样。
这个应用里,它为了实现增强网页里面的功能,它不单是加入了很多内存这样的函数,而且加入很多插件。可以通过插件ID调用这里面很多插件,我们从它可以调用的插件里发现了很引起我们兴趣的东西就是这个,就是它本身自己带了一个支付功能的应用。
所以我们把这个ID传入,作为walletPaySDK传进去,它的参数里有一个很重要的OrderID,也就是说我们可以生成订单,但是我们先不支付,然后我们作为payload发过去,可能导致支付风险,就是可能支付别人那去了。
当然,这个最终使用还需要依赖其他条件。但是这个例子充分说明了,对于支付类的SDK详细显示购买商品和金额是一个特别必要的事。之前交大他们做第三方支付研究时,也介绍过这一点。这个例子也是本地和远程都可以打的。
这里我们做个阶段性小结:之前我们演示的例子里主要是以文件读作为我们利用的一个点,我们自然而然想到文件写是不是也可以。同时,除了我们利用通过API这样的方式、这样的JSBridge,还有没有其他JS接口可以供我们调用?在攻击方式上,我们刚才讲了通过URLScheme和DeepLink的形式进行远程攻击,那还有没有其他方式?
下面看下JS代码执行情况,就是在ios9还是ios8,引入了一个更为安全、性能更高的WKWeView。WKWeView有一个新特性是支持JS注入。在下面的代码里,可以把沙箱的这个JS插入到页面中,以这个加载到前端页面都会自动引入我们一个JS进去。
被插入的JS长什么样子?大概这样子,它也会定义很多函数。对JS程序而言,它可以当作自己实现的函数一样进行调用。JS会驻留在所有网页中,它会定义很多函数,效果类似于JSBridge,如果存在刚才案例中的情况,我们依然可以通过调用这样的函数进行渗透。
如果刚刚我们备注的JS存在沙箱或者远程的话,结合文件写等等,我们把本身给重写了,就可以实现XSS。如果热更新,当然,热更新现在已经被苹果禁止了,如果有热更新的话也可以实现效果。
下面看一个代码执行的例子,这个代码执行的例子里,它首先是判断这个scheme是不是等于这个字符串,如果等于的话它会post这个,这个到底是什么?
它是ios里一个应用内部通信的机制。在之前都会调用这个server的API,类似于把这个先注册,注册时会传输这个action东西叫什么名字,它对应的是什么,也就是说它要执行的函数是什么。
所以在这个例子里,我们只要传输URLScheme,它这个字符串进去,就会导致这个函数被执行。但这里其实并没有实现任意代码执行,但是如果被执行是一个非常敏感的函数或者结合其他漏洞的话,还是可以进行利用的。
刚刚我们说了远程触发,除此之外还有一个经常被别人忽略的二维码扫描,这是普遍大家都在使用的一个方式,APP里面定义一个名以后,它可能在很多地都用同一个名进行加载,只不过这个URL传输的方式不一样。
所以利用二维码扫描也是可以做到的,在之前我们团队有同事做过类似的活动,扫描二维码就可以把你的照片发送给远端服务器。
再一个是后台触发,但是这个利用难度就比较大了,因为后台权限是很难拿到的。但是这个比较有意思的一点是什么?如果把攻击向量放在后台,它是可以打所有前端,也就是说你的服务器可以打这个业务里所有的客户端,这是触发方式里比较有意思的一点。
五
总结以及开发建议
说一下怎样发现漏洞以及发现漏洞的思路是什么。首先,看看URLScheme定义情况,如果有的话我们就看它的回调函数以及重点关注它有没有加载URL这样的操作。
另外,看看它有没有通过二维码或者其他方式加载任意URL。最后,对它我们可以调用的接口进行分析,看有没有可利用的点。
在开发定义里面说几点:iOS生态比安卓做得好多,也一定程度上保证了安全性,但是应用安全并不能完全依赖于系统安全。第二,对于URLScheme使用建议保证最少原则,因为增加一个URLScheme就相当于增加一个接口,增加一个接口就增加一个风险。
在混合开发模式中,这种核心敏感的操作建议使用原生代码进行开发。外部可调用接口一定要严格过滤调用者,确保可信。比如刚刚的例子,如果我们在那个位置的check过不了的话,后面也就无从谈起了。
最后,对WebView本身是一个非常容易出现漏洞的组件之一,实际开发中又很难避开它去不用,所以在开发中要注意已知和未知的安全漏洞。
2、2019 SDC 议题回顾 | 新威胁对策:TSCM 技术反窃密
3、2019 SDC 议题回顾 | 安全研究视角看macOS平台EDR安全能力建设
4、2019 SDC 议题回顾 | Android容器和虚拟化
5、2019 SDC 议题回顾 | 基于云数据的司法取证技术
6、2019 SDC 议题回顾 | Android漏洞检测沙箱的设计与实现
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com
↙点击“阅读原文”,查看更多精彩